﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Gezel.Model;

#endregion

namespace Weazel.Gezel.Parser
{
  /// <summary>
  /// directive: display_directive | finish_directive
  /// </summary>
  public static class ParseDirective
  {
    public static readonly Set<Token> First = 
      new Set<Token>().Union(ParseFinishDirective.First).Union(ParseDisplayDirective.First);

    public static Expression Parse(Set<Token> follow, Tokenizer t)
    {
      Set<Token> current = 
        new Set<Token>().Union(ParseFinishDirective.First).Union(ParseDisplayDirective.First);

      t.Check(follow.Union(current));

      if (ParseFinishDirective.First.Contains(t.CurrentToken))
      {
        return ParseFinishDirective.Parse(follow, t);
      }
      else
      {
        return ParseDisplayDirective.Parse(follow, t);        
      }
    }
  }

  public class ParseOptionDirective
  {
    public static readonly Set<Token> First =
      new Set<Token>(Token.OptionDirective);

    public static void Parse(Set<Token> follow, Tokenizer t, Model.Model model)
    {
      OptionDirective optionsDirective = new OptionDirective();
      optionsDirective.StartPosition = t.CurrentStartPosition;

      t.Accept(Token.OptionDirective, follow.Union(Token.Str));

      if (t.CurrentToken == Token.Str)
        optionsDirective.Value = t.CurrentString;

      optionsDirective.EndPosition = t.CurrentEndPosition;
      t.Accept(Token.Str, follow);

      model.OptionDirectives.Add(optionsDirective);
    }
  }

  public class ParseFinishDirective
  {
    public static readonly Set<Token> First = new Set<Token>(
      Token.FinishDirective
    );

    public static Expression Parse(Set<Token> follow, Tokenizer t)
    {			
      t.Accept(Token.FinishDirective, follow);
      return new DirectiveExpression(
				ExpressionType.FinishDirective, 
				t.CurrentStartPosition, 
				t.CurrentEndPosition
			);
    }
  }

  /// <summary>
  /// display_directive: DISPLAY RBRACKET_OPEN display_el_list RBRACKET_CLOSE
  /// </summary>
  public class ParseDisplayDirective
  {
    public static readonly Set<Token> First = new Set<Token>(
      Token.Display
    );

    public static Expression Parse(Set<Token> follow, Tokenizer t)
    {
      Set<Token> current = new Set<Token>(
        // Token.Display,
        Token.LeftParenthesis,
        Token.RightParenthesis
      );

      current = current.Union(ParseDisplayElementList.First);
		
			Position startPosition = t.CurrentStartPosition;

      t.Accept(Token.Display, follow.Union(current));

      current.Remove(Token.LeftParenthesis);
      t.Accept(Token.LeftParenthesis, follow.Union(current));

      current.Remove(ParseDisplayElementList.First);     
      Expression[] arr = ParseDisplayElementList.Parse(follow.Union(current), t);

			Position endPosition = t.CurrentEndPosition;
      t.Accept(Token.RightParenthesis, follow);

      return new DirectiveExpression(
				ExpressionType.DisplayDirective, 
				arr, 
				startPosition, 
				endPosition
			);
    }
  }

  public static class ParseDisplayElementList
  {
    public static readonly Set<Token> First = 
      new Set<Token>().Union(ParseDisplayElement.First);

    public static Expression[] Parse(Set<Token> follow, Tokenizer t)
    {
      List<Expression> result = new List<Expression>();

      Set<Token> current = new Set<Token>(
        Token.Comma
      );

      current = current.Union(ParseDisplayElement.First);

      bool done = false;

      do
      {        
        result.Add( ParseDisplayElement.Parse(follow.Union(current), t) );

        // the result may have null elements which must be removed (if parsing failed)        
        List<Expression> r = new List<Expression>();
        foreach (Expression e in result)
          if (e != null)
            r.Add(e);

        result = r;

        t.Check(follow.Union(current));

        if (t.CurrentToken == Token.Comma)
        {
          t.Accept(Token.Comma, follow.Union(current));         
        }
        else
        {
          done = true;
        }

      } while (!done);

      return result.ToArray();
    }
  }

  /// <summary>
  /// display_el: string_term 
  ///         | ID 
  ///         | conditional_expr 
  ///         | DPNAME 
  ///         | SFGNAME 
  ///         | CYCLENUM 
	///   			| HEXDISPLAY 
  ///         | BINDISPLAY 
  ///         | DECDISPLAY 
  ///         ;
  /// </summary>
  public class ParseDisplayElement
  {
    public static readonly Set<Token> First = new Set<Token>(
      Token.Str,
      Token.Identifier,
      Token.DpDirective,
      Token.SfgDirective,
      Token.Cycle,
      Token.Hex,
      Token.Bin,
      Token.Dec,
      Token.ToggleDirective
    ).Union(ParseConditionalExpression.First);

    public static Expression Parse(Set<Token> follow, Tokenizer t)
    {
      // branch

      // note: First == current
      t.Check(follow.Union(First));

      // Note: Identifiers are handled as expressions 
      if (t.CurrentToken == Token.Str)
      {
				Expression e = new LeafExpression(ExpressionType.Str, t.CurrentString, t.CurrentStartPosition, t.CurrentEndPosition);
        t.Accept(Token.Str, follow);
        return e;
      }       
      else if (t.CurrentToken == Token.DpDirective)
      {
				Expression e = new DirectiveExpression(ExpressionType.DatapathDirective, t.CurrentStartPosition, t.CurrentEndPosition);
        t.Accept(Token.DpDirective, follow);
        return e;
      }
      else if (t.CurrentToken == Token.SfgDirective)
      {
				Expression e = new DirectiveExpression(ExpressionType.SfgDirective, t.CurrentStartPosition, t.CurrentEndPosition);
        t.Accept(Token.SfgDirective, follow);
        return e;
      }
      else if (t.CurrentToken == Token.Cycle)
      {
				Expression e = new DirectiveExpression(ExpressionType.CycleDirective, t.CurrentStartPosition, t.CurrentEndPosition);
        t.Accept(Token.Cycle, follow);
        return e;
      }
      else if (t.CurrentToken == Token.Hex)
      {
				Expression e = new DirectiveExpression(ExpressionType.HexDirective, t.CurrentStartPosition, t.CurrentEndPosition);
        t.Accept(Token.Hex, follow);
        return e;
      }
      else if (t.CurrentToken == Token.Bin)
      {
				Expression e = new DirectiveExpression(ExpressionType.BinDirective, t.CurrentStartPosition, t.CurrentEndPosition);
        t.Accept(Token.Bin, follow);
        return e;
      }
      else if (t.CurrentToken == Token.Dec)
      {
				Expression e = new DirectiveExpression(ExpressionType.DecDirective, t.CurrentStartPosition, t.CurrentEndPosition);
        t.Accept(Token.Dec, follow);
        return e;
      }
      else if (t.CurrentToken == Token.ToggleDirective)
      {
        Expression e = new DirectiveExpression(ExpressionType.ToggleDirective, t.CurrentStartPosition, t.CurrentEndPosition);
        t.Accept(Token.ToggleDirective, follow);
        return e;
      }
      else if (ParseConditionalExpression.First.Contains(t.CurrentToken))
      {
        return ParseConditionalExpression.Parse(follow, t);
      }
      else
      {
        t.Errors.Add(
          new UnexpectedTokenParseError(
            t.CurrentPosition,
            t.CurrentToken,
            follow
          )
        );

        return null;
      }
    }
  }
}
